/******************************************************************************
 Copyright (c) 2008-2022 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

 REVISION:      $Revision: #9 $
 ******************************************************************************/

#include "csr_synergy.h"

#include "csr_bt_cm_dm.h"
#include "csr_bt_cm_rfc.h"
#include "csr_bt_cm_util.h"
#include "csr_bt_cm_l2cap.h"
#include "csr_bt_cm_sdc.h"
#include "csr_bt_cm_dm_sc_ssp_handler.h"
#include "csr_bt_cm_events_handler.h"

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
#include "csr_bt_cm_bnep.h"
#endif

#ifdef CSR_BT_INSTALL_CM_CACHE_PARAMS

/* --- Static functions --- */

/* CM cache table related functions */
static CsrBool csrBtCmCacheParamAddrMatch(CsrCmnListElm_t *elem, void *data)
{
    dmCacheParamEntry *entry = (dmCacheParamEntry *) elem;
    CsrBtDeviceAddr *addr = (CsrBtDeviceAddr *) data;

    return (CsrBtBdAddrEq(addr, &entry->deviceAddr));
}

static void csrBtCmCacheParamFlush(CsrCmnListElm_t *elem, void *data)
{
    dmCacheParamEntry *entry = (dmCacheParamEntry *) elem;

    entry->pageScanMode = HCI_PAGE_SCAN_MODE_DEFAULT;
    entry->pageScanRepMode = HCI_PAGE_SCAN_REP_MODE_R2;

    CSR_UNUSED(data);
}

static dmCacheParamEntry *csrBtCmGetEntryInCacheParamTable(cmInstanceData_t *cmData,
                                                           CsrBtDeviceAddr *deviceAddr)
{
    return (dmCacheParamEntry *)CsrCmnListSearch((CsrCmnList_t *) &cmData->dmVar.dmCacheParamTable,
                                                 csrBtCmCacheParamAddrMatch,
                                                 deviceAddr);
}

static dmCacheParamEntry *csrBtCmInsertEntryInCacheParamTable(cmInstanceData_t *cmData,
                                                              CsrBtDeviceAddr *devAddr)
{
    dmCacheParamEntry *entry = csrBtCmGetEntryInCacheParamTable(cmData,
                                                                devAddr);

    if (!entry)
    {
        entry = (dmCacheParamEntry *) CsrCmnListElementAddLast((CsrCmnList_t *) &cmData->dmVar.dmCacheParamTable,
                                                               sizeof(dmCacheParamEntry));
    }

    return (entry);
}

#ifdef ENABLE_SHUTDOWN
void CsrBtCmRemoveCacheParamTable(cmInstanceData_t *cmData)
{
    CsrCmnListDeinit((CsrCmnList_t *) &cmData->dmVar.dmCacheParamTable);
}
#endif

/* DM communication related functions */
static void csrBtCmWriteCachedClockOffset(CsrBtDeviceAddr devAddr,
                                          CsrUint16 clockOffset)
{
    dm_write_cached_clock_offset_req(&devAddr, clockOffset, NULL);
}

static void csrBtCmWriteCachedPageMode(CsrBtDeviceAddr devAddr,
                                       page_scan_mode_t pageScanMode,
                                       page_scan_rep_mode_t pageScanRepMode)
{
    dm_write_cached_page_mode_req(&devAddr,
                                  pageScanMode,
                                  pageScanRepMode,
                                  NULL);
}

/* Handler functions for resuming prior activities */
#ifndef EXCLUDE_CSR_BT_RFC_MODULE
static void csrBtCmDmWriteCachedPageModeCfmRfcHandler(cmInstanceData_t *cmData,
                                                      cmRfcConnElement *theRfcElement)
{
    if (theRfcElement && theRfcElement->cmRfcConnInst)
    {
        cmRfcConnInstType *theLogicalLink = theRfcElement->cmRfcConnInst;

        if (!cmData->rfcVar.cancelConnect)
        {
            CSR_BT_CM_STATE_CHANGE(theLogicalLink->state,
                                   CSR_BT_CM_RFC_STATE_CONNECT);
            CsrBtCmRfcStartInitiateConnection(cmData, theLogicalLink);
            CsrBtCmDmLocalQueueHandler();
        }
        else
        { /* The application has requested to cancel the connection */
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
            CsrUint8 numOfConnection = CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData,
                                                                                 theLogicalLink->deviceAddr);

            if (numOfConnection)
            { /* Currently there is at least one more connection attach to the
                 device address. Ensure that SNIFF is supported */
                cmData->smVar.arg.result.code = CSR_BT_RESULT_CODE_CM_CANCELLED;
                cmData->smVar.arg.result.supplier = CSR_BT_SUPPLIER_CM;
                CsrBtCmDmWriteLpSettingsAllMsgSend(cmData,
                                                   theLogicalLink->deviceAddr,
                                                   KEEP_CURRENT_PLAYER);
                CSR_BT_CM_STATE_CHANGE(theLogicalLink->state,
                                       CSR_BT_CM_RFC_STATE_CONNECT);
            }
            else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
            { /* There is no other connection attach to the device address.
                 Inform the application. */
                CsrBtCmConnectCfmMsgSend(cmData,
                                         CSR_BT_RESULT_CODE_CM_CANCELLED,
                                         CSR_BT_SUPPLIER_CM);
                CsrBtCmDmLocalQueueHandler();
            }

        }
    }
    else
    {
        if (theRfcElement)
        { /* If theRfcElement == NULL then this function has already been called */
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
        }
        CsrBtCmDmLocalQueueHandler();
    }
}
#endif

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
static void csrBtCmDmWriteCachedPageModeCfmL2caHandler(cmInstanceData_t *cmData,
                                                       cmL2caConnElement *theL2caElement)
{
    if (theL2caElement && theL2caElement->cmL2caConnInst)
    {
        if (cmData->l2caVar.cancelConnect)
        {
            CsrBtCmL2caConnectCancelCleanup(cmData, theL2caElement, TRUE);
        }
#ifndef EXCLUDE_CSR_BT_CM_LEGACY_PAIRING_DETACH
        else
        {
            cmL2caConnInstType *l2CaConnection= theL2caElement->cmL2caConnInst;
            /* Check whether we need to apply the legacy
             * pairing ACL detach work around */
            if (!CsrBtCmL2caCheckLegacyDetach(cmData, l2CaConnection))
            {
                CsrBtCml2caAutoConnectSetup(cmData, theL2caElement);
                CsrBtCmDmLocalQueueHandler();
            }
        }
#endif /* !EXCLUDE_CSR_BT_CM_LEGACY_PAIRING_DETACH */
    }
    else
    {
        if (theL2caElement)
        { /* If theL2caElement == NULL then this function has allready been called */
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
        }
        CsrBtCmDmLocalQueueHandler();
    }
}
#endif

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
static void csrBtCmDmWriteCachedPageModeCfmBnepHandler(cmInstanceData_t *cmData)
{
    CsrUint8 theIndex;
    bnepTable *bnepConnection;

    theIndex = returnIndexToThisBdAddr(cmData, cmData->dmVar.cacheTargetDev);
    bnepConnection = &(cmData->bnepVar.connectVar[theIndex]);

    if (!cmData->bnepVar.cancelConnect)
    {
        CSR_BT_CM_STATE_CHANGE(bnepConnection->state,
                               CSR_BT_CM_BNEP_STATE_CONNECT);
        bnepConnection->actualMode = CSR_BT_ACTIVE_MODE;
        CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectState, CM_BNEP_CONNECT);
        CsrBtBnepConnectReqSend(cmData->bnepVar.connectReqFlags,
                                CsrBtBdAddrToEtherAddr(&bnepConnection->deviceAddr));
        CsrBtCmDmLocalQueueHandler();
    }
    else
    {
        cmData->smVar.arg.result.code = CSR_BT_RESULT_CODE_CM_CANCELLED;
        cmData->smVar.arg.result.supplier = CSR_BT_SUPPLIER_CM;

        CsrBtCmBnepConnectIndMsgSend(cmData,
                                     cmData->bnepVar.appHandle,
                                     ID_EMPTY,
                                     CsrBtBdAddrToEtherAddr(&bnepConnection->deviceAddr),
                                     0,
                                     0,
                                     0,
                                     cmData->smVar.arg.result.code,
                                     cmData->smVar.arg.result.supplier);

#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
        if (CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData,
                                                      bnepConnection->deviceAddr))
        { /* Currently there is at least one more connection attach to the
             device address. Ensure that SNIFF is supported         */
            CsrBtCmDmWriteLpSettingsAllMsgSend(cmData,
                                               bnepConnection->deviceAddr,
                                               KEEP_CURRENT_PLAYER);
            CSR_BT_CM_STATE_CHANGE(bnepConnection->state,
                                   CSR_BT_CM_BNEP_STATE_CONNECT_CFM);
        }
        else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
        { /* There is no other connection attach to the device address.
             Inform the application, restore the local service manager
             queue, and clear up the bnep connection table                  */
            CsrBtCmBnepClearBnepTableIndex(bnepConnection);
            CsrBtCmDmLocalQueueHandler();
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
        }
        CsrBtCmDmUpdateAndClearCachedParamReqSend(bnepConnection->deviceAddr);
    }
}
#endif

static void csrBtCmDmWriteCachedPageModeCfmSdcHandler(cmInstanceData_t *cmData)
{
    CsrBtCmSdcStartHandler(cmData, cmData->dmVar.cacheTargetDev);
    CsrBtCmDmLocalQueueHandler();
}

static void csrBtCmDmWriteCachedPageModeCfmScHandler(cmInstanceData_t *cmData)
{
    if (cmData->dmVar.cancel)
    {
        switch (cmData->dmVar.dmMsgTypeInProgress)
        {
            case CSR_BT_CM_ACL_OPEN_REQ:
            {
                CsrBtTypedAddr addrt;
                cmPendingMsg_t *pendingMsg;

                addrt.addr = cmData->dmVar.cacheTargetDev;
                addrt.type = CSR_BT_ADDR_PUBLIC;

                pendingMsg = CsrBtCmAclOpenPendingMsgGet(cmData,
                                                         CSR_BT_CM_IFACEQUEUE,
                                                         addrt,
                                                         0);
                if (pendingMsg)
                {
                    CsrPCmnListRemove(cmData->pendingMsgs,
                                      (CsrCmnListElm_t *) pendingMsg);
                }

                if (cmData->dmVar.lockMsg == CSR_BT_CM_ACL_OPEN_REQ)
                {
                    CsrBtCmDmLocalQueueHandler();
                }
                break;
            }

            case CSR_BT_CM_SM_BONDING_REQ:
                CsrBtCmDmBondingCfm(cmData,
                                    &cmData->dmVar.cacheTargetDev,
                                    HCI_ERROR_CONN_TERM_LOCAL_HOST);
                break;

            default:
                CsrBtCmGeneralException(CSR_BT_CM_PRIM,
                                        cmData->dmVar.dmMsgTypeInProgress,
                                        cmData->globalState,
                                        "");
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                /* DM queue unlocked in outer scope */
                break;
        }
        cmData->dmVar.cancel = FALSE;
    }
    else
    {
        CSR_BT_CM_STATE_CHANGE(cmData->dmVar.state, CSR_BT_CM_DM_STATE_CONNECT);

        switch (cmData->dmVar.dmMsgTypeInProgress)
        {
            case CSR_BT_CM_ACL_OPEN_REQ:
            {
                TYPED_BD_ADDR_T ad;
                ad.addr = cmData->dmVar.cacheTargetDev;
                ad.type = CSR_BT_ADDR_PUBLIC;
                dm_acl_open_req(&ad, 0, NULL);

                if (cmData->dmVar.lockMsg == CSR_BT_CM_ACL_OPEN_REQ)
                {
                    CsrBtCmDmLocalQueueHandler();
                }
                break;
            }

            case CSR_BT_CM_SM_BONDING_REQ:
            {
                TYPED_BD_ADDR_T ad;
                ad.addr = cmData->dmVar.cacheTargetDev;
                ad.type = CSR_BT_ADDR_PUBLIC;
                dm_sm_bonding_req(&ad, NULL);
                break;
            }

            default:
                CsrBtCmGeneralException(CSR_BT_CM_PRIM,
                                        cmData->dmVar.dmMsgTypeInProgress,
                                        cmData->globalState,
                                        "");
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                /* DM queue unlocked in outer scope */
                break;
        }
    }

    CsrBtCmDmLocalQueueHandler();
}

static void csrBtCmDmWriteCachedPageModeCfmAppHandler(cmInstanceData_t *cmData)
{
    if (cmData->dmVar.dmMsgTypeInProgress == CSR_BT_CM_ACL_OPEN_REQ)
    {
        TYPED_BD_ADDR_T ad;

        ad.addr = cmData->dmVar.cacheTargetDev;
        ad.type = CSR_BT_ADDR_PUBLIC;
        dm_acl_open_req(&ad, 0, NULL);
    }
    else
    { /* This must never happen */
        CsrBtCmGeneralException(CSR_BT_CM_PRIM,
                                cmData->dmVar.dmMsgTypeInProgress,
                                cmData->globalState,
                                "");

        /* DM queue unlocked in outer scope */
        CsrBtCmDmLocalQueueHandler();
    }
}

static void csrBtCmDmWriteCachedPageModeCfmCmHandler(cmInstanceData_t *cmData)
{
    if (cmData->dmVar.dmMsgTypeInProgress == CSR_BT_CM_READ_REMOTE_NAME_REQ)
    {
        if (cmData->dmVar.cancel)
        {
            CsrBtCmReadRemoteNameCfmSend(cmData,
                                         cmData->dmVar.appHandle,
                                         cmData->dmVar.readNameDeviceAddr,
                                         CSR_BT_RESULT_CODE_CM_CANCELLED,
                                         CSR_BT_SUPPLIER_CM);
            cmData->dmVar.readingName = FALSE;
            cmData->dmVar.cancel = FALSE;
            CsrBtCmDmUpdateAndClearCachedParamDirect(cmData,
                                                     cmData->dmVar.readNameDeviceAddr);
        }
        else
        {
            cmData->dmVar.readingName = TRUE;
            dm_hci_remote_name_request(&cmData->dmVar.cacheTargetDev, NULL);
        }
    }
    else
    {
        /* UNKNOWN ACTION */
        /* This never happens */
    }
}

/* --- Externally available functions --- */
void CsrBtCmDmWriteCachedPageModeCfmHandler(cmInstanceData_t *cmData)
{
    switch (cmData->smVar.activePlayer)
    {
#ifndef EXCLUDE_CSR_BT_RFC_MODULE
        case RFC_PLAYER:
            csrBtCmDmWriteCachedPageModeCfmRfcHandler(cmData,
                                                      CM_RFC_ELEMENT_ACTIVE(cmData));
            break;
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
        case L2CAP_PLAYER:
            csrBtCmDmWriteCachedPageModeCfmL2caHandler(cmData,
                                                       CM_L2CA_ELEMENT_ACTIVE(cmData));
            break;
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
        case BNEP_PLAYER:
            csrBtCmDmWriteCachedPageModeCfmBnepHandler(cmData);
            break;
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

        case SDC_PLAYER:
            csrBtCmDmWriteCachedPageModeCfmSdcHandler(cmData);
            break;

        case CM_PLAYER:
            csrBtCmDmWriteCachedPageModeCfmCmHandler(cmData);
            break;

        case SC_PLAYER:
            csrBtCmDmWriteCachedPageModeCfmScHandler(cmData);
            break;

        case APP_PLAYER:
            csrBtCmDmWriteCachedPageModeCfmAppHandler(cmData);
            break;

        default:
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
            CsrBtCmDmLocalQueueHandler();
            break;
    }
}

static void csrBtCmDmWriteCacheParamsReqSend(CsrUint8 activePlayer,
                                             CsrBtDeviceAddr devAddr,
                                             CsrUint16 clockOffset,
                                             page_scan_mode_t pageScanMode,
                                             page_scan_rep_mode_t pageScanRepMode)
{
    CsrBtCmDmWriteCacheParamsReq *prim;

    prim = (CsrBtCmDmWriteCacheParamsReq *) CsrPmemAlloc(sizeof(*prim));

    prim->type = CSR_BT_CM_DM_WRITE_CACHE_PARAMS_REQ;
    prim->activePlayer = activePlayer;
    prim->devAddr = devAddr;
    prim->clockOffset = clockOffset;
    prim->pageScanMode = pageScanMode;
    prim->pageScanRepMode = pageScanRepMode;
    CsrBtCmPutMessage(CSR_BT_CM_IFACEQUEUE, prim);
}

static CsrBool cacheParamsKnown(cmInstanceData_t *cmData,
                                CsrBtDeviceAddr devAddr,
                                CsrUint16 *clockOffset,
                                page_scan_mode_t *pageScanMode,
                                page_scan_rep_mode_t *pageScanRepMode)
{
    dmCacheParamEntry * entryCacheTable = csrBtCmGetEntryInCacheParamTable(cmData,
                                                                           &devAddr);

    if (entryCacheTable)
    {
        *clockOffset = entryCacheTable->clockOffset;
        *pageScanMode = entryCacheTable->pageScanMode;
        *pageScanRepMode = entryCacheTable->pageScanRepMode;
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

CsrBool CsrBtCmDmWriteKnownCacheParams(cmInstanceData_t *cmData,
                                       CsrBtDeviceAddr devAddr,
                                       CsrBtCmPlayer player)
{
    CsrUint16 clockOffset;
    page_scan_mode_t pageScanMode;
    page_scan_rep_mode_t pageScanRepMode;

    if (cacheParamsKnown(cmData,
                         devAddr,
                         &clockOffset,
                         &pageScanMode,
                         &pageScanRepMode))
    {
        /* start writing cache params. via DM queue */
        csrBtCmDmWriteCacheParamsReqSend(player,
                                         devAddr,
                                         clockOffset,
                                         pageScanMode,
                                         pageScanRepMode);

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

static void csrBtCmDmWriteCacheParamsDirect(cmInstanceData_t *cmData,
                                            CsrBtDeviceAddr devAddr,
                                            CsrUint16 clockOffset,
                                            page_scan_mode_t pageScanMode,
                                            page_scan_rep_mode_t pageScanRepMode,
                                            CsrBtCmPlayer activePlayer,
                                            CsrBtCmPrim dmMsgTypeInProgress)
{
    cmData->smVar.activePlayer = activePlayer;
    cmData->dmVar.dmMsgTypeInProgress = dmMsgTypeInProgress;

    /* Store the page-mode information */
    cmData->dmVar.pageScanMode = pageScanMode;
    cmData->dmVar.pageScanRepMode = pageScanRepMode;
    cmData->dmVar.cacheTargetDev = devAddr;

    /* start sending parameters directly to DM */
    /* send clock offset now and send page mode information on clock offset cfm */
    csrBtCmWriteCachedClockOffset(devAddr,
                                  clockOffset);
}

CsrBool CsrBtCmDmWriteKnownCacheParamsDirect(cmInstanceData_t *cmData,
                                             CsrBtDeviceAddr devAddr,
                                             CsrBtCmPlayer player,
                                             CsrBtCmPrim dmMsgTypeInProgress)
{
    CsrUint16 clockOffset;
    page_scan_mode_t pageScanMode;
    page_scan_rep_mode_t pageScanRepMode;

    if (cacheParamsKnown(cmData,
                         devAddr,
                         &clockOffset,
                         &pageScanMode,
                         &pageScanRepMode))
    {
        /* start writing cache params. via DM queue */
        csrBtCmDmWriteCacheParamsDirect(cmData,
                                        devAddr,
                                        clockOffset,
                                        pageScanMode,
                                        pageScanRepMode,
                                        player,
                                        dmMsgTypeInProgress);

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

void CsrBtCmDmWriteCacheParamsReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmDmWriteCacheParamsReq *cmPrim = (CsrBtCmDmWriteCacheParamsReq *) cmData->recvMsgP;

    csrBtCmDmWriteCacheParamsDirect(cmData,
                                    cmPrim->devAddr,
                                    cmPrim->clockOffset,
                                    cmPrim->pageScanMode,
                                    cmPrim->pageScanRepMode,
                                    cmPrim->activePlayer,
                                    0);
}

void CsrBtCmDmUpdateAndClearCachedParamReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmDmUpdateAndClearCachedParamReq *cmPrim = (CsrBtCmDmUpdateAndClearCachedParamReq *) cmData->recvMsgP;

    CsrBtCmDmUpdateAndClearCachedParamDirect(cmData, cmPrim->devAddr);
}

void CsrBtCmDmWriteCachedClockOffsetCfmHandler(cmInstanceData_t *cmData)
{
    /* The DM has confirmed that it has received a clock offset */
    /* There is no check to see if it was success or failure */
    /* because in case of failure performance will only degrade */
    /* a bit, hence proceed with the sequence  by sending the */
    /* page scan mode and page scan rep mode */

    csrBtCmWriteCachedPageMode(cmData->dmVar.cacheTargetDev,
                               cmData->dmVar.pageScanMode,
                               cmData->dmVar.pageScanRepMode);
}

void CsrBtCmDmStoreCacheParams(cmInstanceData_t *cmData,
                               CsrBtDeviceAddr devAddr,
                               CsrUint16 clockOffset,
                               page_scan_mode_t pageScanMode,
                               page_scan_rep_mode_t pageScanRepMode)
{

    dmCacheParamEntry * newEntry = csrBtCmInsertEntryInCacheParamTable(cmData,
                                                                       &devAddr);

    newEntry->deviceAddr = devAddr;
    newEntry->clockOffset = clockOffset;
    newEntry->pageScanMode = pageScanMode;
    newEntry->pageScanRepMode = pageScanRepMode;
}

void CsrBtCmDmClearParamCacheCfmHandler(cmInstanceData_t *cmData)
{
    CsrBtCmDmLocalQueueHandler();
}

void CsrBtCmDmHciReadClockOffsetCompleteHandler(cmInstanceData_t *cmData)
{
    DM_HCI_READ_CLOCK_OFFSET_CFM_T *dmPrim;

    dmPrim = (DM_HCI_READ_CLOCK_OFFSET_CFM_T *) cmData->recvMsgP;

    if (dmPrim->status == HCI_SUCCESS)
    {
        dmCacheParamEntry *newEntry = csrBtCmInsertEntryInCacheParamTable(cmData,
                                                                          &(dmPrim->bd_addr));

        newEntry->deviceAddr = dmPrim->bd_addr;
        newEntry->clockOffset = dmPrim->clock_offset;
    }
    dm_clear_param_cache_req(&(dmPrim->bd_addr), NULL);
}

/* Loopback functions to lock the DM queue */

void CsrBtCmDmUpdateAndClearCachedParamReqSend(CsrBtDeviceAddr devAddr)
{
    CsrBtCmDmUpdateAndClearCachedParamReq *prim;

    prim = (CsrBtCmDmUpdateAndClearCachedParamReq *) CsrPmemAlloc(sizeof(*prim));

    prim->type = CSR_BT_CM_DM_UPDATE_AND_CLEAR_CACHED_PARAM_REQ;
    prim->devAddr = devAddr;
    CsrBtCmPutMessage(CSR_BT_CM_IFACEQUEUE, prim);
}

void CsrBtCmDmUpdateAndClearCachedParamDirect(cmInstanceData_t *cmData,
                                              CsrBtDeviceAddr devAddr)
{
    CsrBool linkExists = FALSE;
    cmData->dmVar.cacheTargetDev = devAddr;

#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    if (CsrBtCmRfcFindRfcConnElementFromDeviceAddrState(cmData,
                                                        &devAddr,
                                                        CSR_BT_CM_RFC_STATE_CONNECTED))
    {
        linkExists = TRUE;
    }
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    if (!linkExists)
    {
        if (CM_FIND_L2CA_ELEMENT(CsrBtCmL2caFindL2caConnElementFromConnectedSDeviceAddr,
                                 &devAddr))
        {
            linkExists = TRUE;
        }
    }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    if (!linkExists)
    {
        CsrUintFast8 i;

        for (i = 0; i < CSR_BT_MAX_NUM_OF_SIMULTANEOUS_BNEP_CONNECTIONS; i++)
        { /* Search through the BNEP connection table */

            if (CsrBtBdAddrEq(&(devAddr),
                              &(cmData->bnepVar.connectVar[i].deviceAddr)))
            {
                if (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED)
                { /* Thie given address is right. */
                    linkExists = TRUE;
                }
            }
        }
    }
#endif

    if (linkExists && cmData->dmVar.lockMsg != CSR_BT_CM_READ_REMOTE_NAME_REQ)
    {
        dm_hci_read_clock_offset(&devAddr, NULL);
    }
    else
    {
        dm_clear_param_cache_req(&devAddr, NULL);
    }
}

void CsrBtCmFlushCmCacheStopTimer(cmInstanceData_t *cmData)
{
    if (cmData->dmVar.cacheFlushTimerId != 0)
    {/* Cancel the timer if it is already running */
        CsrSchedTimerCancel(cmData->dmVar.cacheFlushTimerId, NULL, NULL);
        cmData->dmVar.cacheFlushTimerId = 0;
    }
}

static void csrBtCmFlushCmCacheTimeout(CsrUint16 dummy, void *data)
{
    cmInstanceData_t *cmData = (cmInstanceData_t *) data;

    CsrCmnListIterate((CsrCmnList_t *) &cmData->dmVar.dmCacheParamTable,
                      csrBtCmCacheParamFlush,
                      NULL);

    cmData->dmVar.cacheFlushTimerId = 0; /* Timer was fired */
    CSR_UNUSED(dummy);
}

void CsrBtCmFlushCmCacheStartTimer(cmInstanceData_t *cmData)
{
    /* Stop the timer if it is already running - should not be running at this point */
    CsrBtCmFlushCmCacheStopTimer(cmData);

    /* Start the timer */
    cmData->dmVar.cacheFlushTimerId = CsrSchedTimerSet(CM_DM_CACHE_FLUSH_TIMEOUT,
                                                       csrBtCmFlushCmCacheTimeout,
                                                       0,
                                                       (void * ) cmData);
}

#endif /* CSR_BT_INSTALL_CM_CACHE_PARAMS */
